#ifndef _SPREAD_HH_
#define _SPREAD_HH_
#include "State.h"
#include "Location.h"
#include <queue>


struct Ploc : public Location
{
  unsigned depth;

  Ploc(Location loc, unsigned _depth) : 
    Location(loc.row,loc.col), 
    depth(_depth) 
  { }

};


template<typename _Action>
void spread(State & s, Location loc, unsigned maxdepth, _Action __action)
{
  std::vector<std::vector<bool> > visited(s.rows, std::vector<bool>(s.cols, false));

  // push starting location
  std::queue<Ploc> q;
  q.push(Ploc(loc,0));
  visited[loc.row][loc.col] = true;
 
  while(!q.empty())
    {
      Ploc ploc = q.front();
      q.pop();
      if(!__action(s,ploc)) break;


      if(ploc.depth<maxdepth)
	{
	  for(int d=0; d<TDIRECTIONS; d++)
	    {
	      Location newloc = s.getLocation(ploc, d);
	      if(!s.location(newloc).isWater and !visited[newloc.row][newloc.col])
		{
		  q.push(Ploc(newloc,ploc.depth+1));
		  visited[newloc.row][newloc.col] = true;
		}
	    }
	}
    }
}



template<typename _Action>
void fly(State & s, Location loc, unsigned maxdepth, _Action __action)
{
  std::vector<std::vector<bool> > visited(s.rows, std::vector<bool>(s.cols, false));

  // push starting location
  std::queue<Ploc> q;
  q.push(Ploc(loc,0));
  visited[loc.row][loc.col] = true;
 
  while(!q.empty())
    {
      Ploc ploc = q.front();
      q.pop();
      if(!__action(s,ploc)) break;


      if(ploc.depth<maxdepth)
	{
	  for(int d=0; d<TDIRECTIONS; d++)
	    {
	      Location newloc = s.getLocation(ploc, d);
	      if(!visited[newloc.row][newloc.col])
		{
		  q.push(Ploc(newloc,ploc.depth+1));
		  visited[newloc.row][newloc.col] = true;
		}
	    }
	}
    }
}








struct update_value_lin_t
{
  double w;

  update_value_lin_t(double _w) : w(_w) {}
  
  bool operator()(State & s, Ploc & ploc)
  {
    s.location(ploc).value += w / (1.0 + ploc.depth);
    return true;
  }

};


struct update_value_circle_t
{
  double w;
  unsigned depth;

  update_value_circle_t(double _w,unsigned _depth) : w(_w),depth(_depth) {}
  
  bool operator()(State & s, Ploc & ploc)
  {
    if(ploc.depth >= depth)
      s.location(ploc).value += w / (1.0 + ploc.depth - depth);
    return true;
  }

};



struct update_value_disc_t
{
  double w;

  update_value_disc_t(double _w,unsigned _depth) : w(_w) {}
  
  bool operator()(State & s, Ploc & ploc)
  {
    s.location(ploc).value += w;
    return true;
  }

};




void set_values(State & s, 
		double foodw, double friendw, double hillw, double myhillw);


#endif /* _SPREAD_HH_ */
